package com.sharp.core.voxel;

import java.util.Arrays;

import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;

public class VoxelArray extends VoxelData implements IsoSurface{

	private int width, height, depth;
	private final int widthXheight;
	private Voxel fullArray[];
	private Voxel visibleVoxels[];
	
	private int index(int x, int y, int z) {
		return z*widthXheight + y*width + x;
	}
	
	public VoxelArray(int width, int height, int depth) {
		this.width = width;
		this.height = height;
		this.depth = depth;
		this.widthXheight = width*height;
		this.fullArray = new Voxel[widthXheight * depth]; 
	}
	
	public Voxel getVoxel(int x, int y, int z) {
		return fullArray[index(x, y, z)];
	}
	
	public void setVoxel(int x, int y, int z, Voxel v) {
		fullArray[index(x, y, z)] = v;
	}
	
	public void merge(VoxelArray other, int X, int Y, int Z) {
		int myIndex = index(X, Y, Z);
		int otherIndex = index(0, 0, 0);
		int maxX = Math.min(width, X + other.width);
		int maxY = Math.min(height, Y + other.height);
		int maxZ = Math.min(depth, Z + other.depth);
		
		int stepRow = (width - other.width);
		int stepDepth = (height - other.height) * width;
		for ( int z = Z; z < maxZ; ++z ) {
			for ( int y = Y; y < maxY; ++y ) {
				for ( int x = X; x < maxX; ++x ) {
					if ( other.fullArray[otherIndex] != null )
						fullArray[myIndex] = other.fullArray[otherIndex];
					++myIndex;
					++otherIndex;
				}
				myIndex += stepRow;
			}
			myIndex += stepDepth;
		}
	}
	
	@Override
	public Iterable<Voxel> getVoxels() {
		return Arrays.asList(fullArray);
	}

	@Override
	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	@Override
	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	@Override
	public int getDepth() {
		return depth;
	}

	public void setDepth(int depth) {
		this.depth = depth;
	}

	@Override
	public String toString() {
		return width+"x"+height+"x"+depth;
	}

	@Override
	public GridCell getCell(float x, float y, float z, float cellSize) {
		if ( x+1 >= width || y+1 >= height || z+1 >= depth )
			return null;
		Voxel[] voxels = new Voxel[8];
		/*voxels[0] = getVoxel((int)x, (int)y, (int)z);
		voxels[1] = getVoxel((int)(x+cellSize), (int)y, (int)z);
		voxels[2] = getVoxel((int)(x+cellSize), (int)y, (int)(z+cellSize));
		voxels[3] = getVoxel((int)x, (int)y, (int)(z+cellSize));
		voxels[4] = getVoxel((int)x, (int)(y+cellSize), (int)z);
		voxels[5] = getVoxel((int)(x+cellSize), (int)(y+cellSize), (int)z);
		voxels[6] = getVoxel((int)(x+cellSize), (int)(y+cellSize), (int)(z+cellSize));
		voxels[7] = getVoxel((int)x, (int)(y+cellSize), (int)(z+cellSize));*/
		voxels[0] = getVoxel((int)x, (int)y, (int)z);
		voxels[1] = getVoxel((int)(x+cellSize), (int)y, (int)z);
		voxels[2] = getVoxel((int)(x+cellSize), (int)(y+cellSize), (int)z);
		voxels[3] = getVoxel((int)x, (int)(y+cellSize), (int)z);
		voxels[4] = getVoxel((int)x, (int)y, (int)(z+cellSize));
		voxels[5] = getVoxel((int)(x+cellSize), (int)y, (int)(z+cellSize));
		voxels[6] = getVoxel((int)(x+cellSize), (int)(y+cellSize), (int)(z+cellSize));
		voxels[7] = getVoxel((int)x, (int)(y+cellSize), (int)(z+cellSize));
		
		GridCell cell = new GridCell(new Vector3f(x, y, z), 1.0f);
		//boolean isIn = voxel != null;
		ColorRGBA color = null;
		for ( int i = 0; i < 8; ++i ) {
			if ( voxels[i] != null ) {
				cell.setValue(i, 1);
				if ( color == null )
					color = new ColorRGBA(voxels[i].getColor());
				else
					color.addLocal(voxels[i].getColor()).multLocal(0.5f);
			} else
				cell.setValue(i, 0);
		}
		cell.setColor(color);
		return cell;
	}

	@Override
	public float getFieldDepth() {
		return depth;
	}

	@Override
	public float getFieldHeight() {
		return height;
	}

	@Override
	public float getFieldWidth() {
		return width;
	}

	@Override
	public ColorRGBA getColor(float x, float y, float z, float cellSize) {
		return getVoxel((int)x, (int)y, (int)z).getColor();
	}

	/*@Override
	public Voxel findIntersection(VoxelObject me, Ray rayIn) {
		Ray  ray;
		if ( rayIn.getT() > 0 )
			ray = new Ray(rayIn, rayIn.getT());
		else
			ray = rayIn;
		
		float rayDirX  = ray.getDir().getX();
		float rayDirY  = ray.getDir().getY();
		float rayDirZ  = ray.getDir().getZ();
		
		float rayPosX  = ray.getOrigin().getX() - me.getPos().getX();
		float rayPosY  = ray.getOrigin().getY() - me.getPos().getY();
		float rayPosZ  = ray.getOrigin().getZ() - me.getPos().getZ();
		
		int mapX = (int) rayPosX;
		int mapY = (int) rayPosY;
		int mapZ = (int) rayPosZ;
		
		
		float maxX, maxY, maxZ;
		int stepX, stepY, stepZ;
		
		if ( rayDirX < 0 ) {
			stepX = -1;
			maxX = (rayPosX - mapX);
		} else {
			stepX = 1;
			maxX = (mapX+1-rayPosX);
		}
		
		if ( rayDirY < 0 ) {
			stepY = -1;
			maxY = (rayPosY - mapY);
		} else {
			stepY = 1;
			maxY = (mapY+1-rayPosY);
		}
		
		if ( rayDirZ < 0 ) {
			stepZ = -1;
			maxZ = (rayPosZ - mapZ);
		} else {
			stepZ = 1;
			maxZ = (mapZ+1-rayPosZ);
		}
		
		float deltaX = 1/rayDirX * stepX;	// függölegestől függelegesik
		float deltaY = 1/rayDirY * stepY; 	// vizszintestől vizszintesig
		float deltaZ = 1/rayDirZ * stepZ;
		maxX *= deltaX;
		maxY *= deltaY;
		maxZ *= deltaZ;
		
		Voxel voxel = null;
		Voxel.Side side = null;
		while( true ) {
			//Vars.set(Vars.INTERSECT_PER_FRAME, Vars.<Integer>get(Vars.INTERSECT_PER_FRAME)+1);
			Vars.g++;
			if ( maxX < maxY ) {
				if ( maxZ < maxX ) {
					maxZ += deltaZ;
					mapZ += stepZ;
					side = Voxel.Side.FRONT;
				} else {
					maxX += deltaX;
					mapX += stepX;
					side = Voxel.Side.LEFT;
				}
			} else if ( maxZ < maxY ) {
					maxZ += deltaZ;
					mapZ += stepZ;
					side = Voxel.Side.FRONT;
			} else {
					maxY += deltaY;
					mapY += stepY;
					side = Voxel.Side.TOP;
			}
			if ( mapX >= width || mapX < 0 || 
					mapY >= height || mapY < 0 ||
					mapZ >= depth || mapZ < 0 ) {
				break;
			}
			if ( (voxel=getVoxel(mapX, mapY, mapZ)) != null )
				break;
		}
		if ( voxel == null )
			return null;
		return voxel;
	}*/
}
